import {_instance} from "./Utils";
import {IndexdbStatus, innerDB} from "./Const";
import Table from "./Table";

export default class Database{
    private name: string;
    private version: number|undefined = 1;
    public connect : any; //数据库连接
    readonly events: any = {
        process: (status: IndexdbStatus) => {
        }, //数据库过程事件
        success: () => {
            console.debug("success")
        },//数据库打开事件
        close: () => {
            console.debug("close")
        },//数据库关闭
        change: () => {
            console.debug("change")
        },//版本改变
        error: () => {

        }//数据库打开链接错误或者数据库错误
    };
    constructor (name:string, version?:number) {
        this.name = name
        this.version = version;
        this.connect = null
    }

    on(event:any, fn?:Function) {
        if (!_instance(event, Object)) {
            Object.keys(event).forEach((e) => this.on(e, event[e]))
        } else {
            if (!(event in this.events)) {
                throw new Error(`Unsupport event type ${event}!`);
            }
            this.events[event.toString()] = fn;
        }
        return this
    }

    isOpen () {
        return this.connect != null
    }

    open (name:string, version?:number) {
        this.name = name = name || this.name;
        this.version = version || this.version;
        const request:any = innerDB.open(name, version);
        this.events.process(IndexdbStatus.CONNECT_ING);
        request.onerror = (e: any) => {
            const error = e.target.error;
            this.events.process(IndexdbStatus.CONNECT_ERROR);
            this.events.error.call(this, error)
        };
        request.onclose = function (e: any) {
            this.events.process(IndexdbStatus.CONNECT_CLOSE);
            this.events.close.call(this, e.target.result)
        };
        return new Promise((resolve) => {
            request.onsuccess=(e:any) => {
                this.connect = e.target.result
                this.events.process(IndexdbStatus.CONNECT_SUCCESS)
                this.connect.onerror = this.connect.onabort = (e:any) => {
                    const error = e.target.error
                    this.events.process(IndexdbStatus.DATABASE_ERROR);
                    this.events.error.call(this, error);
                };
                this.events.success.call(this, this.connect);
                resolve(this);
            }
            request.onupgradeneeded = (e: any) => {
                this.events.process(IndexdbStatus.CONNECT_SUCCESS);
                this.connect = e.target.result;
                this.events.change.call(this, this.connect);
                resolve(this)
            }
        });
    }

    drop () {
        this.close();
        return new Promise((resolve, reject) => {
            let request = innerDB.deleteDatabase(this.name);
            request.onerror = function (event: any) {
                reject(event)
            };
            request.onsuccess = function (event: any) {
                resolve(event)
            };
        })
    }

    close () {
        if(this.isOpen()) {
            this.events.process(IndexdbStatus.CONNECT_CLOSE);
            this.connect.close();
            this.events.close.call(this, this.connect);
            this.connect = null;
        }
    }

    /**
     * 只能在open的时候调用
     *      name:'xx',
     *      primaryKey: 'id',
     *      autoIncrement: true|false，
     *      indexes: [
     *          {name: 'xxx', column: 'id', unique: true|false, multiEntry: true|false},
     *      ],
     *      data: [{}]
     *      }
     * @param tab
     */
    createTable(tab:string|object) {
        let table:any = tab;
        if (_instance(tab, String)) {
            table = {name: tab}
        }
        return new Table(table, this)
    }

    /**
     * 只能在open的时候调用
     * @param tableName
     */
    dropTable(tableName:string) {
        return this.connect.deleteObjectStore(tableName)
    }

    table(tableName:string) {
        if(!this.exists(tableName)) {
            throw new Error(`table ${tableName} not exists!`)
        }
        return new Table({name: tableName}, this)
    }

    exists(table:string) {
        const tables = this.tables();
        return tables && tables.contains(table);
    }

    tables() {
        return this.connect['objectStoreNames']
    }
}
